package main

import (
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

//图书
type Book struct {
	Id      bson.ObjectId `bson:"_id"`    //主键
	Name    string        `bson:"name"`   //图书名称
	Price   float32       `bson:"price"`  //价格
	Authors []Author      `bson:"author"` //作者
	Tags    []string      `bson:"tags"`   //标签
	Press   string        `bson:"press"`  //出版社
}

//作者
type Author struct {
	Name string `bson:"name"` //姓名
	Sex  string `bson:"sex"`  //性别
}

const (
	BookCollection = "book"
)

func NewBook(name string, price float32, authors []Author, tags []string, press string) *Book {
	b := &Book{
		Name:    name,
		Price:   price,
		Authors: authors,
		Tags:    tags,
		Press:   press,
	}

	b.Id = bson.NewObjectId()

	return b
}

func insert() {
	//声明为interface数组，才能批量插入
	books := make([]interface{}, 0, 10)

	book1 := NewBook("高等数学上", 37.70, []Author{{"同济大学数学系", ""}}, []string{"数学", "大学数学", "高等数学"}, "高等教育出版社")
	books = append(books, book1)

	book2 := NewBook("TCP/IP详解卷1：协议", 45.00,
		[]Author{{"W.Richard Stevens", "男"}, {"范建华", "男"}, {"胥光辉", "男"}, {"张涛", "男"}, {"谢希仁", "男"}},
		[]string{"计算机网络", "网络协议", "编程"}, "机械工业出版社")
	books = append(books, book2)

	book3 := NewBook("Python程序设计开发宝典", 69.00, []Author{{"董付国", "男"}}, []string{"程序设计", "编程", "python"}, "清华大学出版社")
	books = append(books, book3)

	book4 := NewBook("汇编语言", 36.00, []Author{{"王爽", ""}}, []string{"程序设计", "编程", "汇编"}, "清华大学出版社")
	books = append(books, book4)

	book5 := NewBook("SparkSQL入门与实践指南", 49.00, []Author{{"纪涵", "女"}, {"靖晓文", "女"}, {"赵政达", "男"}},
		[]string{"程序设计", "编程", "spark", "spark sql"}, "清华大学出版社")
	books = append(books, book5)

	expr1 := func(c *mgo.Collection) error {
		return c.Insert(books...)
	}

	err := WithMongoCollection(BookCollection, expr1)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println("插入成功")
}

func deleteAll() {
	expr := func(c *mgo.Collection) error {
		_, err := c.RemoveAll(bson.M{})
		return err
	}

	err := WithMongoCollection(BookCollection, expr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println("完成数据清空")
}

//单个条件 =
func equal() {
	query := bson.M{"name": "Python程序设计开发宝典"}
	searchAll(query)
}

//单个条件 >
func gt() {
	query := bson.M{"price": bson.M{"$gt": 40.0}}
	searchAll(query)
}

//单个条件 <
func lt() {
	query := bson.M{"price": bson.M{"$lt": 40.0}}
	searchAll(query)
}

//单个条件 >=
func gte() {
	query := bson.M{"price": bson.M{"$gte": 45}}
	searchAll(query)
}

//单个条件 <=
func lte() {
	query := bson.M{"price": bson.M{"$lte": 36}}
	searchAll(query)
}

//单个条件 !=
func ne() {
	query := bson.M{"press": bson.M{"$ne": "清华大学出版社"}}
	searchAll(query)
}

// and
//select * from table where price<=50 and press='清华大学出版社'
func and() {
	query := bson.M{"price": bson.M{"$lte": 50}, "press": "清华大学出版社"}
	searchAll(query)
}

// or
//select * from table where press='高等教育出版社' or press='清华大学出版社'
func or() {
	query := bson.M{"$or": []bson.M{bson.M{"press": "高等教育出版社"}, bson.M{"name": "Python程序设计开发宝典"}}}
	searchAll(query)
}

// 单个key的or查询可以使用 in 或 nin
func in() {
	query := bson.M{"press": bson.M{"$in": []string{"清华大学出版社", "机械工业出版社"}}}
	searchAll(query)
}

func nin() {
	query := bson.M{"press": bson.M{"$nin": []string{"清华大学出版社", "机械工业出版社"}}}
	searchAll(query)
}

// not
func not() {
	query := bson.M{"press": bson.M{"$not": bson.RegEx{Pattern: "^清华", Options: "i"}}}
	searchAll(query)
}

//正则查询
//$regex操作符的使用
//
//$regex操作符中的option选项可以改变正则匹配的默认行为，它包括i, m, x以及s四个选项，其含义如下
//
//i 忽略大小写，{<field>{$regex/pattern/i}}，设置i选项后，模式中的字母会进行大小写不敏感匹配。
//m 多行匹配模式，{<field>{$regex/pattern/,$options:'m'}，m选项会更改^和$元字符的默认行为，分别使用与行的开头和结尾匹配，而不是与输入字符串的开头和结尾匹配。
//x 忽略非转义的空白字符，{<field>:{$regex:/pattern/,$options:'m'}，设置x选项后，正则表达式中的非转义的空白字符将被忽略，同时井号(#)被解释为注释的开头注，只能显式位于option选项中。
//s 单行匹配模式{<field>:{$regex:/pattern/,$options:'s'}，设置s选项后，会改变模式中的点号(.)元字符的默认行为，它会匹配所有字符，包括换行符(\n)，只能显式位于option选项中。
//
//使用$regex操作符时，需要注意下面几个问题:
//
//i，m，x，s可以组合使用，例如:{name:{$regex:/j*k/,$options:"si"}}
//在设置索弓}的字段上进行正则匹配可以提高查询速度，而且当正则表达式使用的是前缀表达式时，查询速度会进一步提高，例如:{name:{$regex: /^joe/}

//字符串模糊查询  开头包含
func beginWith() {
	query := bson.M{"name": bson.M{"$regex": bson.RegEx{Pattern: "^高等", Options: "i"}}}

	searchAll(query)
}

//模糊查询 包含
func contains() {
	//query := bson.M{"name": bson.M{"$regex": "开发", "$options": "$i"}}
	query := bson.M{"name": bson.M{"$regex": bson.RegEx{Pattern: "开发", Options: "i"}}}
	searchAll(query)
}

//模糊查询 结尾包含
func endWith() {
	query := bson.M{"name": bson.M{"$regex": bson.RegEx{Pattern: "指南$", Options: "i"}}}
	searchAll(query)
}

//数组查询，数组中的元素可能是单个值数据，也可能是子文档
//针对单个值数据
//满足数组中单个值
func arrayMatchSingle() {
	query := bson.M{"tags": "编程"}
	searchAll(query)
}

//同时满足所有条件，不要求顺序
func arrayMatchAll() {
	query := bson.M{"tags": bson.M{"$all": []string{"程序设计", "编程", "python"}}}
	searchAll(query)
}

//查询特定长度
func arrayMatchSize() {
	query := bson.M{"tags": bson.M{"$size": 4}}
	searchAll(query)
}

//满足特定索引下条件
//数组索引从0开始，我们匹配第二项就用tags.1作为键
func arrayMatchIndex() {
	query := bson.M{"tags.1": "编程"}
	searchAll(query)
}

//精确查找，数量，顺序都要满足
func arrayMatch() {
	query := bson.M{"tags": []string{"数学", "大学数学", "高等数学"}}
	searchAll(query)
}

//针对与数组中的子文档
//满足单个价值
func subDocMatchSingle() {
	query := bson.M{"author.name": "纪涵"}
	searchAll(query)
}

//elementMath
func subDocMatchElement() {
	query := bson.M{"author": bson.M{"$elemMatch": bson.M{"name": "谢希仁", "sex": "男"}}}
	searchAll(query)
}

//聚合操作
//sum
//count
//max
//min
//avg
//group
//order
//limit
//project

//记数
func count() {
	var count int
	expr := func(c *mgo.Collection) error {
		var err error
		count, err = c.Find(bson.M{}).Count()
		return err
	}

	err := WithMongoCollection(BookCollection, expr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println(count)
}

//去重
func distinct() {
	var result []interface{}
	expr := func(c *mgo.Collection) error {
		return c.Find(bson.M{}).Distinct("press", &result)
	}

	err := WithMongoCollection(BookCollection, expr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println(result)

}

//求和
func sum() {
	query := []bson.M{
		//bson.M{"$match": bson.M{"press": "清华大学出版社"}},
		bson.M{"$project": bson.M{"_id": 0, "price": 1, "press": 1}},
		bson.M{"$group": bson.M{"_id": "$press", "totalPrice": bson.M{"$sum": "$price"}}},
	}

	var result []bson.M
	expr := func(c *mgo.Collection) error {
		return c.Pipe(query).All(&result)
	}

	err := WithMongoCollection(BookCollection, expr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println(result)
}

//最大值和最小值
func maxAndMin() {
	query := []bson.M{
		bson.M{"$group": bson.M{"_id": nil,"maxPrice": bson.M{"$max": "$price"},"minPrice":bson.M{"$min":"$price"}}},
	}

	var result []bson.M
	expr := func(c *mgo.Collection) error {
		return c.Pipe(query).All(&result)
	}

	err := WithMongoCollection(BookCollection, expr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println(result)
}

//平均值
func avg(){
	query := []bson.M{
		bson.M{"$group": bson.M{"_id": nil,"avgPrice": bson.M{"$avg": "$price"}}},
	}

	var result []bson.M
	expr := func(c *mgo.Collection) error {
		return c.Pipe(query).All(&result)
	}

	err := WithMongoCollection(BookCollection, expr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println(result)
}



func searchAll(query bson.M) {
	var books []Book
	expr := func(c *mgo.Collection) error {
		return c.Find(query).All(&books)
	}

	err := WithMongoCollection(BookCollection, expr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	if len(books) == 0 {
		fmt.Println("未找到记录...")
		return
	}

	for _, book := range books {
		fmt.Println(book)
	}

}
